package cwinsor.com.leaf_raking;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

abstract public class Person {

	/////////////
	// values
	protected String valFirstName;
	protected String valLastName;
	protected Integer valPower;
	protected String valGender;
	protected String valVarsityNovCox;
	protected Integer valGrade;

	protected Map<WorkShift,Integer> valAvailLast = new HashMap<WorkShift,Integer>();
	protected Map<WorkShift,Integer> valAvailCurrent = new HashMap<WorkShift,Integer>();
	protected Map<WorkShift,TeamNumber> valAssignmentCurrent = new HashMap<WorkShift,TeamNumber>();
	protected Map<WorkShift,TeamChange> valAddDrop = new HashMap<WorkShift,TeamChange>();

	protected String valPhoneC;
	protected String valPhoneH;
	protected String valStreet;

	/**
	 * constructor
	 */
	public Person() {

		setFirstName("x");
		setLastName("x");
		setGender("x");
		setVarsityNovCox("x");

		setPower(-100);
		setGrade(-100);

		for (WorkShift w: WorkShift.asList()) {
			setAvailabilityLast(w,0);
			setAvailabilityCurrent(w,0);
			setAssignmentCurrent(w, TeamNumber.TEAM_UNASSIGNED);
			setAddDrop(w, TeamChange.NO_CHANGE);
		}

		setPhoneC("x");
		setPhoneH("x");
		setStreet("x");
	}


	/*
	 * get type
	 */
	public abstract String getType();


	///////////////////
	// data getters

	public String getFirstName() { return valFirstName; }
	public String getLastName() { return valLastName; }
	public Integer getPower() { return valPower; }
	public String getGender() { return valGender; }

	public String getVarsityNovCox() { return valVarsityNovCox; }
	public Integer getGrade() { return valGrade; }
	public String getPhoneC() { return valPhoneC; }
	public String getPhoneH() {	return valPhoneH; }
	public String getStreet() {	return valStreet; }

	public Integer getLastAvailability(WorkShift workShift) {
		return valAvailLast.get(workShift);
	}
	public Integer getCurrentAvailability(WorkShift workShift) {
		return valAvailCurrent.get(workShift);
	}
	public TeamNumber getAssignmentCurrent(WorkShift workShift) {
		return valAssignmentCurrent.get(workShift);
	}
	public TeamChange getAddDrop(WorkShift workShift) {
		return valAddDrop.get(workShift);
	}

	public String getFirstInitial() {
		return (valFirstName.substring(0, 1) + ".");
	}

	///////////
	// fields that are calculated on the fly

	/*
	 * get the change in availability
	 * this is calculated as the
	 * difference between available current and available last
	 */
	public Integer getAvailDelta(WorkShift workShift) {
		return getCurrentAvailability(workShift)-getLastAvailability(workShift);
	}

	/*
	 * get degree of change
	 * a reduction operation
	 * computed as the sum of the absolute values of available last and available current
	 */
	public Integer getAvailTotalChange() {
		Integer totalChange = 0;
		for (WorkShift workShift : WorkShift.asList()) {
			totalChange += Math.abs(getAvailDelta(workShift));
		}
		return totalChange;
	}

	/*
	 * get happiness
	 * computed as:
	 * if assigned to that day
	 *   if desired (currentAvailability > 0) add 1 to happiness
	 *   if not desired (currentAvailability < 0) deduct the currentAvailability amount
	 * so for example
	 *   if assigned to a day where currentAvailability = -2 it would deduct 2 from happiness
	 *   if assigned to a where currentAvailability = +2 it would add 1 to happiness 
	 */
	public Integer getHappiness() {
		Integer totalHappiness = 0;
		for (WorkShift workShift : WorkShift.asList()) {
			if (getAssignmentCurrent(workShift) != TeamNumber.TEAM_UNASSIGNED) {
				if (getCurrentAvailability(workShift) > 0) {
					totalHappiness++;
				}
				else {
					totalHappiness -= getCurrentAvailability(workShift);
				}
			}
		}
		return totalHappiness;
	}

	///////////////////
	///////////////////
	// data setters

	public void setFirstName(String val) { valFirstName = val; }
	public void setLastName(String val) { valLastName = val; }
	public void setPower(Integer val) {
		if (!(  (val.equals(1)) ||
				(val.equals(2)) ||
				(val.equals(3)) ||
				(val.equals(4)) ||
				(val.equals(5)) ||
				(val.equals(-100)))) {
			Msg.fatal(dS + ": problem setting power " + val.toString());
		}
		valPower = val;
	}
	public void setGender(String val) { valGender = val; }

	public void setVarsityNovCox(String val) {
		if (!(  val.equals("v") || val.equals("n") || val.equals("c")|| val.equals("x"))) {
			Msg.fatal(dS + ": problem setting varsity/nov/cox" + val);
		}
		valVarsityNovCox = val;
	}
	public void setGrade(Integer val) {
		if (!( ((val>=7) && (val<=12)) ||
				(val==-100))) {
			Msg.fatal(dS + ": problem setting grade " + val.toString());
		}
		valGrade= val;
	}

	public void setPhoneC(String val) { valPhoneC = val; }
	public void setPhoneH(String val) { valPhoneH = val; }
	public void setStreet(String val) { valStreet = val; }
	/*
	 * a utility function to range check availability fields
	 */
	public void rangeCheckAvailability(String msg, Integer val) {
		if (!(  ((val>=-2) && (val<=2)) ||
				(val==-100))) {
			Msg.fatal(msg + " with value " + val.toString());
		}
	}

	public void setAvailabilityLast(WorkShift workShift, Integer val) {
		rangeCheckAvailability(dS + "setLastAvailability", val);
		valAvailLast.put(workShift, val);
	}
	public void setAvailabilityCurrent(WorkShift workShift, Integer val) {
		rangeCheckAvailability(dS + "setCurrentAvailability", val);
		valAvailCurrent.put(workShift, val);
	}
	public void setAssignmentCurrent(WorkShift workShift, TeamNumber teamNumber) {
		valAssignmentCurrent.put(workShift, teamNumber);
	}
	public void setAddDrop(WorkShift workShift, TeamChange teamChange) {
		valAddDrop.put(workShift, teamChange);
	}



	public enum Format {
		LONG_FORM, SHORT_FORM, CHANGE_SUMMARY, FOR_APPOINTMENT_SHEET
	}

	/*
	 * toStringCommaDelimited
	 * return fields as a comma-delimited string
	 */
	//	public String toStringCommaDelimitedAllFields(Format fmt) {
	//		Map<Integer,String> theFieldsObject = new HashMap<Integer,String>();
	//
	//		FileType fT = new FileTypeState();
	//
	//		fT.toStringCommaDelimitedAllFieldsMid(theFieldsObject, fmt, this);
	//
	//		// concatenate fields into a comma-delimited string
	//		String temp = new String("");
	//		Msg.fatal(dS + ": TEMPORARILY COMMENTED OUT");
	/*
for (int i=0; i<=fT.colEnd(); i++) {
			if (theFieldsObject.get(i) == null) {
				temp = temp + ",";
			}
			else {
				temp = temp
						+ ((i==0)? "" : ",")
						+ theFieldsObject.get(i);
			}
		}
	 */
	//		return temp;
	//	}

	/*
	 * take updated schedule preferences for this person
	 */
	//	public void takeInUpdates(Person rUpdate) {
	//
	//		// if the user put in NO updates then propagate their earlier schedule
	//		Boolean aChange = false;
	//		for (WorkShift workShift : WorkShift.asList()) {
	//			if (!rUpdate.getCurrentAvailability(workShift).equals(0)) {
	//				aChange = true;
	//			}
	//		}
	//
	//		// current availability becomes last availability
	//		for (WorkShift workShift : WorkShift.asList()) {
	//
	//			this.valAvailLast.put(workShift, this.valAvailCurrent.get(workShift));
	//			// take value from the update (if user made any change)
	//			if (aChange) {
	//				this.valAvailCurrent.put(workShift, rUpdate.valAvailCurrent.get(workShift));
	//			}
	//		}
	//	}


	/*
	 * this method applies the Add/Drop to the current schedule, and clears the Add/Drop
	 */
	public void applyAddDrops() {
		for (WorkShift workShift : WorkShift.asList()) {
			TeamChange teamChange = getAddDrop(workShift);
			TeamNumber teamNumber = getAssignmentCurrent(workShift).updateTeam(teamChange);
			setAssignmentCurrent(workShift, teamNumber);
			setAddDrop(workShift, TeamChange.NO_CHANGE);
		}
	}


	/*
	 * comparison - based on first name, last name
	 */
	public Boolean equals(Person that) {
		return this.getFirstName().equals(that.getFirstName()) && this.getLastName().equals(that.getLastName());
	}


	// debug
	String dS = this.getClass().getSimpleName();
	Integer dL = new Integer(1);
}

